<?php
class Daemon {
	private $run = null;
	
	private $maxProcesses = 0;
	
	private $pidfile = null;
	
	public $running_resource = null;
	
	private $running = false;
	
	function __construct($maxProcesses, $pidfile = null) {
		$this->maxProcesses = $maxProcesses;
		$this->pidfile = $pidfile;
		
		if($this->maxProcesses == 0) {
			return;
		}

		if(!extension_loaded('pcntl') || !extension_loaded('posix')) {
			echo "pcntl and posix are required!\n";
			exit(-1);
		}

		if($this->pidfile) {
			$pid = pcntl_fork();
			if($pid == -1) {
				exit(-1);
			} else if($pid) {
				file_put_contents($this->pidfile, "$pid");
				exit(0);
			}
		}
		
		$daemon = $this;
		
		pcntl_signal(SIGINT, function($signal) use ($daemon) {
			$daemon->setRunning(false);
			echo "killed " . posix_getpid() . "\n";
		});
		pcntl_signal(SIGTERM, function($signal) use ($daemon) {
			$daemon->setRunning(false);
			echo "killed " . posix_getpid() . "\n";
		});
	}
	
	function isRunning() {
		return $this->running;
	}
	
	function setRunning($running) {
		return $this->running = $running;
	}
	
	function run_onchild($run) {
		$this->running = true;
		
		$fun = $run;
		$fun(0, $this);
	}
	
	function run($run) {
		if($this->maxProcesses == 0) {
			$this->run_onchild($run);
			return;
		}
		
		$this->running = true;
		$this->running_resource = tmpfile();
		
		$children = array();
		$is_child = false;
		
		$daemon = $this;
		
		if(!$is_child) {
			declare(ticks = 1) {
				while($this->isRunning()) {
					if(count($children) < $this->maxProcesses) {
						$pid = pcntl_fork();
						if($pid == -1) {
							echo "error\n";
						} else if($pid) {
							echo "fork $pid\n";
							$children[$pid] = array(
									'createdTime' => microtime(true)
							);
						} else {
							$is_child = true;
							break;
						}
					} else {
						$pid = pcntl_waitpid(-1, $status, WNOHANG | WUNTRACED);
						if($pid > 0) {
							unset($children[$pid]);
							echo "remove $pid\n";
						}
					}
					usleep(200000);
				}
			}

			if(!$is_child) {
				foreach($children as $pid=>$child) {
					posix_kill($pid, SIGTERM);
				}
				
				while(count($children)) {
					$pid = pcntl_waitpid(-1, $status, WUNTRACED);
					echo "exit $pid\n";
					unset($children[$pid]);
				}

				if($this->pidfile && file_exists($this->pidfile)) unlink($this->pidfile);
			}
		}

		if($is_child) {
			$fun = $run;
			$fun(posix_getpid(), $daemon);
		}

	}

}